【Python】標準ライブラリPathlibでおしゃれにPathを表記しよう!
こんにちは。DA事業本部の春田です。
今さらですが、最近Pythonの Pathlib という標準ライブラリを認知しました。
いやはや、めちゃめちゃ良いですねコレ。すでにご存知の方も多いかと思いますが、そのエレガントさに身を任せてご紹介していきます。
- 実行環境:
- macOS Mojave 10.14.6
- Python 3.7.5
※PathlibはPython3.4以降で導入されています
基本
こんな構成のディレクトリ群を例とします。
animals <- I'm here! ├── dogs │ ├── shiba.py │ ├── beagle.txt │ └── pug │ └── buchakawa.json └── cats │ ├── scottish.txt │ └── doraemon.exe └── hamaguchi.py
PathlibはPython3.4以降の標準ライブラリなので、シュパッと import
できます。まずはメインクラスの Path
を入れます。
>>> from pathlib import Path
Path
の引数にパスを含んだ文字列を渡すと、 PosixPath
インスタンスが返されます。(実行環境がWindowsの場合は WindowsPath
ですかね?)このインスタンスで、あんなことやこんなことができるわけですね。存在確認は exists()
メソッドを用います。
>>> dogs = Path('./dogs') >>> dogs PosixPath('dogs') >>> print(dogs) dogs >>> dogs.exists() True
Pathlibの最もエレガントな仕様は、パスを除算記号 /
で繋げられる点だと思います。文字列を format
したり +
で繋げる方法よりも、記述量が減り読みやすくなります。要素の一部に PosixPath
インスタンスが使われていれば、文字列でも繋げられます。
>>> shiba = dogs / 'shiba.py' >>> shiba PosixPath('dogs/shiba.py') >>> shiba.exists() True
shiba
から buchakawa.json
を指したいな、という時には、 parent
もしくは parents
プロパティの出番です。
>>> bucha1 = shiba.parent / 'pug' / 'buchakawa.json' >>> bucha1 PosixPath('dogs/pug/buchakawa.json') >>> bucha2 = shiba.parents[0] / 'pug' / 'buchakawa.json' # スライスで親ディレクトリの位置を指定 >>> bucha2 PosixPath('dogs/pug/buchakawa.json')
ファイル名を取りたい場合は name
、拡張子が欲しい時は suffix
、拡張子を除いたファイル名が欲しい場合は stem
といったプロパティも用意されています。本当にプロパティの種類が豊富ですね。
>>> bucha.name 'buchakawa.json' >>> bucha.suffix '.json' >>> bucha.stem 'buchakawa'
応用
検索の glob
メソッドではジェネレータ生成され、マッチしたPosixPathインスタンスが返されます。
>>> cwd = Path('.') >>> cwd.glob('*') <generator object Path.glob at 0x103e5cad0> >>> [*cwd.glob('*')] [PosixPath('dogs'), PosixPath('cats'), PosixPath('hamaguchi.py')] >>> [*cwd.glob('cats/*.exe')] [PosixPath('cats/doraemon.exe')]
Pathに任意の文字列が含まれているかどうか判定するには、 match
メソッドを使います。右側から一致を調べていく仕様なので、それに合わせたパスも記述する必要があります。
>>> bucha PosixPath('dogs/pug/buchakawa.json') >>> bucha.match('pug') False >>> bucha.match('*/pug') False >>> bucha.match('pug/*') True
僕のお気に入りは relative_to
メソッドです。起点のパスからの相対パスを表現することができます。これを応用すると、任意のファイルに対してディレクトリの付替えなどができます。
>>> bucha PosixPath('dogs/pug/buchakawa.json') >>> pug = Path('dogs/pug') >>> pug PosixPath('dogs/pug') >>> scottish = Path('cats/scottish.txt') >>> scottish PosixPath('cats/scottish.txt') >>> scottish.parent / bucha.relative_to(pug) PosixPath('cats/buchakawa.json')
ファイル名や拡張子を付け替えたい時は、 with_name
や with_suffix
メソッドが使えます。うわぁぁ!拡張子だけ変えたいのにっっ!!なんてことよくありませんか?
>>> bucha.with_name('mechakawa.yaml') PosixPath('dogs/pug/mechakawa.yaml') >>> scottish.with_suffix('.json') PosixPath('cats/scottish.json') >>> scottish.with_suffix('json') # '.'が必要です Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py", line 843, in with_suffix raise ValueError("Invalid suffix %r" % (suffix)) ValueError: Invalid suffix 'json'
その他、スクリプト内でファイルシステムに触れるのはちょっと怖いな、という時は Path
クラスの代わりに PurePath
クラスを使用すると機能が制限されます。
>>> from pathlib import PurePath >>> shiba = PurePath('dogs/shiba.py') >>> shiba.name 'shiba.py' >>> shiba.exists() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'PurePosixPath' object has no attribute 'exists' >>> shiba.glob('../') Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'PurePosixPath' object has no attribute 'glob'
凄いシンプルなライブラリですが、組み合わせ次第で可能性は無限大!!という感じですね。ただし、Pathクラスは文字列ではないので、PathLikeオブジェクトに対応していない関数やメソッドでは、一旦 str()
などを噛ませる必要があります。その点だけちょっと面倒ですね。。
最後に
ご紹介しきれなかった機能もあるので、興味のある方は公式ドキュメントもご参照ください!